import { popTarget, pushTarget } from './dep'
import {util} from '../util'

let id = 0
class Watcher {// 每次产生一个watcher都有一个唯一的标识
  /**
   * watcher分为三类， 渲染watcher, 用户watcher, 计算属性watcher
   * @param {*} vm 当前组件的实例
   * @param {*} exprOrFn 用户传入的是一个表达式，或者一个函数
   * @param {*} cb 用户传入的回调函数 vm.$watch('msg', cb)
   * @param {*} opts 一些其他参数
   */
  constructor(vm, exprOrFn, cb = () => {}, opts = {}){
    this.vm = vm;
    this.exprOrFn = exprOrFn;
    if (typeof exprOrFn === 'function') {
      this.getter = exprOrFn; // getter 就是new Watcher传入的第二个参数
    }else{
      // 会将vm上对用的key取出来
      this.getter = function(){
        return util.getValue(vm, exprOrFn);
      }
    }

    if (opts.user) { // 标识是用户自己写的watcher
      this.user = true;
    }
    this.lazy = opts.lazy; // 如果这个值为true，说明他是计算属性
    this.dirty = this.lazy;
    this.cb = cb;
    this.deps = [];
    this.depsId = new Set();
    this.opts = opts;
    this.id = id++;
    // 如果是计算属性的话，不会默认调用get方法
    // this.value => watch 方法里面的oldValue
    this.value = this.lazy ? undefined: this.get(); // 默认创建的时候，调用自身的get方法
  }

  get(){
    pushTarget(this);
    let value = this.getter.call(this.vm); // 让当前传入的函数执行
    popTarget()
    return value;
  } 

  update(){ // 如果立即调用，会导致频繁更新，需要批量进行更新
    if (this.lazy) {
      this.dirty = true;
    }else{
      queueWatcher(this);
    }
  }

  addDep(dep){ // 同一个watcher 不应该重复记录dep
    let id = dep.id;
    if (!this.depsId.has(id)) {
      this.depsId.add(id);
      this.deps.push(dep);
      dep.addSub(this);
    }
  }

  depend(){
    let i = this.deps.length;
    while(i--){
      this.deps[i].depend()
    }
  }

  run(){
    // 新值
    let value = this.get();
    if (this.value !== value) {
      this.cb(value, this.value);
    }
  }

  evaluate(){
    this.value = this.get();
    this.dirty = false;
  }
}

let has = {};
let queue = [];
function flushQueue(){
  queue.forEach(watcher => watcher.run());
  has = {};
  queue = [];
}
function queueWatcher(watcher){ // 对重复的watcher进行过滤操作
  let id = watcher.id;
  if (!has[id]) {
    has[id] = true;
    queue.push(watcher); // 相同的watcher只会存一个到queue中

    // 延迟清空队列
    // setTimeout(flushQueue, 0)
    nextTick(flushQueue)
  }
}

let callbacks = []
function flushCallbacks(){
  callbacks.forEach(cb => cb())
}
function nextTick(cb){
  callbacks.push(cb);
  // 要异步刷新这个callbacks
  // 异步是分执行顺序的， 
  let timerFunc = () => {
    flushCallbacks()
  }

  if (Promise) {
    return Promise.resolve().then(timerFunc)
  }

  if (MutationObserver) {
    let observe = new MutationObserver(timerFunc);
    let textNode = document.createTextNode();
    observe.observer(textNode, {characterData: true});
    textNode.textContent = 2;
    return
  }

  if (setImmediate) {
    setImmediate(timerFunc)
    return
  }

  setTimeout(timerFunc, 0)
}

// 渲染使用， 计算属性也要用它， vm.watch 也用它
export default Watcher;